/**
 *
 * 1. 插件   挂载 $store
 * 
 *  

 *  */

let Vue

class Store {
  constructor(options) {


    /**
     * 
     * 将用户传进来的数据  响应式处理
     * 
     * new  Vue()
     * 里面为什么要放data
     * 默认把 data 属性递归一下变成响应式数据
     * 
     * new Vue({
     *   data:{
     *      $$state: state
     *    }
     * })
     * 
     */
    const { state, mutations, actions, getters } = options

    // 1. 处理 mutation  action  getters
    this._mutations = mutations || {}
    this._actions = actions || {}
    this._wrappendGetters = getters || {}

    // 定义computed 选项
    this.getters = {}
    // 借用 computed 实现值的缓存
    const computed = this.registerGetters(state, this);

    this._vm = new Vue({
      data: {
        $$state: state
      },
      computed
    })

    //  确定 this 指向，在确保 commit 和 dispatch 的时候 this 指向不会出问题
    this.commit = this.commit.bind(this)
    this.dispatch = this.dispatch.bind(this)

  }

  get state () {
    return this._vm._data.$$state;
  }

  set state (v) {
    console.error('place user replaceState to reset state');
  }

  commit (type, payload) {
    const entry = this._mutations[type]
    if (!entry) {
      console.error('unkown mutation type ' + type);
    }
    entry(this.state, payload);
  }

  dispatch (type, payload) {
    const entry = this._actions[type]
    if (!entry) {
      console.error('unkown actions type ' + type);
    }
    entry(this, payload);
  }

  registerGetters (state, _this) {
    const computed = {};
    Object.keys(_this._wrappendGetters).forEach(key => {
      const fn = _this._wrappendGetters[key]
      // 将用户传入的 getters 的键，一次绑定给 _vm 的 computed，实现值的缓存功能
      computed[key] = function () {
        return fn(state);
      };

      Object.defineProperty(_this.getters, key, {
        //  getters 中都是只读值
        get () {
          // 取值时，根据 Vue实例的规则，会从 _vm 的 computed 中取到值，并且返回
          return _this._vm[key];
        }
      });
    })
    return computed;
  }
}

function install (_Vue) {
  Vue = _Vue

  Vue.mixin({
    beforeCreate () {
      if (this.$options.store) {
        Vue.prototype.$store = this.$options.store
        // console.log(Vue.prototype.$store);
      }
    }
  })
}

export default { Store, install }